package com.lp.mgt.configs.mybatis_intecepter;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Properties;

import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.MappedStatement.Builder;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import com.lp.mgt.commons.annotation.DataPermission;
import com.lp.mgt.entities.Role;
import com.lp.mgt.entities.User;
import com.lp.mgt.utils.ShrioUtil;

//@Component
@Intercepts({
	@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
	@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
})
public class DataPremissionInterceptor implements Interceptor {
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Object[] args = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) args[0];
        Object parameter = args[1];
        RowBounds rowBounds = (RowBounds) args[2];
        Executor executor = (Executor) invocation.getTarget();
        SqlCommandType  sqlCommandType = mappedStatement.getSqlCommandType();
        CacheKey cacheKey;
        BoundSql boundSql;
        //由于逻辑关系，只会进入一次
        if (args.length == 4) {
            //4 个参数时
            boundSql = mappedStatement.getBoundSql(parameter);
            cacheKey = executor.createCacheKey(mappedStatement, parameter, rowBounds, boundSql);
        }else {
            //6 个参数时
            cacheKey = (CacheKey) args[4];
            boundSql = (BoundSql) args[5];
        }
        String orgSql=boundSql.getSql();
        String newSql=null;
        //反射获得方法信息，判断是否 有注解
        Class<?> classType = Class.forName(mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf(".")));
        String methodName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1, mappedStatement.getId().length());
        for (Method method : classType.getDeclaredMethods()) {
            if (SqlCommandType.SELECT==sqlCommandType &&
            	method.isAnnotationPresent(DataPermission.class) &&
            	methodName.equals(method.getName())) {
	            	DataPermission dp=method.getAnnotation(DataPermission.class);
	            	String alias=dp.alias();
	            	//获取当前用户
	            	User currUser=ShrioUtil.getCurrentUser();
	            	String currDept=currUser.getDid();//当前用户所属部门
	            	StringBuilder appendSql =new StringBuilder();//需要追加的过了sql
	            	//遍历所有角色
	            	for(Role r:currUser.getRoles()) {
	            		switch(r.getDataScope()) {
	            			case "1"://全部数据 跳过
	            				appendSql.append(" 1=1");
	            				break;
	            			case "2"://辖内部门(本部门以及所有子部门)
	            				String txt2="";
	            				if(!"".equals(appendSql.toString())){
	            					txt2=" or";
	            				}
	            				appendSql.append(txt2+" k.create_by in (select uid from mgt_user where did in ( select dept_id from mgt_department where del_flag='0' and (dept_id='"+currDept+"' or FIND_IN_SET('"+currDept+"',ancestors) >0 )))" );
	            				break;
	            			case "3"://本部门
	            				String txt1="";
	            				if(!"".equals(appendSql.toString())) {
	            					txt1=" or";
	            				}
	            				appendSql.append(txt1+" k.create_by in (select uid FROM mgt_user WHERE did='"+currDept+"')");
	            				break;
	            		}
	            	}
	            	newSql="select * from ("+orgSql+") k where"+appendSql;
	            	break;
            }
            newSql=orgSql;
        }
        //通过反射修改sql语句
/*        Field field = boundSql.getClass().getDeclaredField("sql");
        field.setAccessible(true);
        field.set(boundSql, newSql);*/
        BoundSql newBoundSql = new BoundSql(mappedStatement.getConfiguration(), newSql, boundSql.getParameterMappings(), boundSql.getParameterObject());
        MappedStatement newMappedStatement = copyFromMappedStatement(mappedStatement,new BoundSqlSqlSource(newBoundSql));
        args[0]=newMappedStatement;
        return invocation.proceed();
	}  

	private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource)
    {
		MappedStatement.Builder builder = new Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType());

        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
			StringBuilder keyProperties = new StringBuilder();
			for (String keyProperty : ms.getKeyProperties()) {
				keyProperties.append(keyProperty).append(",");
			}
			keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
			builder.keyProperty(keyProperties.toString());
		}
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }
	
	
	   @Override
	    public Object plugin(Object target) {
	        return Plugin.wrap(target, this);
	    }
		 
	    @Override
	    public void setProperties(Properties properties) {
	    }
}
